﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace SyncUdp.Network
{
    using Enum;
    using Event.NetworkEvent;
    using Utility;
    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.Threading;
    using System.Threading.Tasks;

    /// <summary>
    /// 抽象的游戏网络通信
    /// </summary>
    public abstract class AbstractGameNetwork
    {
        /// <summary>
        /// 用于 UDP 通信的对象
        /// </summary>
        protected UdpClient _udpClient;

        /// <summary>
        /// 用于取消线程
        /// </summary>
        protected CancellationTokenSource _cts;

        /// <summary>
        /// 用于接收消息的线程
        /// </summary>
        protected Task _task;

        /// <summary>
        /// json 工具
        /// </summary>
        protected IJsonUtility _jsonUtility;

        /// <summary>
        /// 网络日志工具
        /// </summary>
        protected INetworkLogUtility _logUtility;

        /// <summary>
        /// 只有发送消息时才会用到的网络事件字段
        /// </summary>
        protected NetworkEventField _networkEventField;

        /// <summary>
        /// 本地 IP 地址
        /// </summary>
        public IPEndPoint LocalIP { get => (IPEndPoint)_udpClient.Client.LocalEndPoint; }

        protected AbstractGameNetwork(IPEndPoint ep, string logFileName)
        {
            _udpClient = new UdpClient(ep);
            _cts = new CancellationTokenSource();
            _networkEventField = new NetworkEventField();
            _logUtility = new NetworkLogUtility(logFileName);
            _jsonUtility = IOCContainer.GetInstance<IJsonUtility>();
        }

        protected AbstractGameNetwork(UdpClient udpClient, string logFileName)
        {
            _udpClient = udpClient;
            _cts = new CancellationTokenSource();
            _networkEventField = new NetworkEventField();
            _logUtility = new NetworkLogUtility(logFileName);
            _jsonUtility = IOCContainer.GetInstance<IJsonUtility>();
        }

        public virtual void Start(IGameManager gm = null)
        {
            StartToReceiveMsg();
        }

        public void Close()
        {
            if (_udpClient == null)
                return;
            _cts?.Cancel();
            _udpClient?.Close();
            _logUtility?.Close();
            OnClose();
            //_task?.Dispose();
            _udpClient?.Dispose();
            _cts?.Dispose();
            _udpClient = null;
            _cts = null;
            _logUtility = null;
            _jsonUtility = null;
        }

        /// <summary>
        /// 接收消息
        /// </summary>
        void StartToReceiveMsg()
        {
            if (_task == null)
            {
                _logUtility.Logging($"1. AbstracatGameNetwork 开始接收消息……");
                _task = Task.Run(ReceiveMsg);
                _task.ContinueWith(task =>
                {
                    _logUtility.Logging($"1. AbstracatGameNetwork 错误：{GetType()}{Environment.NewLine}" +
                        $"{task.Exception.InnerException}");
                }, TaskContinuationOptions.OnlyOnFaulted);
            }
        }

        async Task ReceiveMsg()
        {
            UdpReceiveResult res;
            while (_cts.IsCancellationRequested == false)
            {
                try
                {
                    res = await _udpClient.ReceiveAsync();
                    var data = _jsonUtility.GetObjectFromBytesWithUTF8<NetworkEventToSend>(res.Buffer);
                    switch (data.eventType)
                    {
                        case NetworkEventType.TryJoiningGame:
                            OnPlayerTryJoiningGame(res.RemoteEndPoint, data.tryJoinningGameEvent);
                            break;
                        case NetworkEventType.JoinGame:
                            OnPlayerJoinedGame(res.RemoteEndPoint, data.joinGameEventArgs);
                            break;
                        case NetworkEventType.ClientSendsInputMsg:
                            OnClientSendInputs(res.RemoteEndPoint, data.clientInputsEventArgs);
                            break;
                        case NetworkEventType.ServerDispatchesInputs:
                            OnServerDispatchedInputs(data.serverDispathesEventArgs);
                            break;
                        case NetworkEventType.ServerReissuesInputs:
                            OnServerReissuedInputs(data.serverReissuesFramesEventArgs);
                            break;
                        case NetworkEventType.NetworkDoesNotDisconnected:
                            OnNetworkNotDisconnect(res.RemoteEndPoint);
                            break;
                        default:
                            _logUtility.Logging($"1. AbstracatGameNetwork 接收的消息枚举错误：{data.eventType}");
                            break;
                    }
                }
                catch (SocketException se)
                {
                    //_logUtility.Logging($"1. AbstracatGameNetwork 接收错误：{GetType()}" +
                    //    $"{Environment.NewLine}{se}");
                }
            }
        }

        #region 需要重写的方法
        protected abstract void OnPlayerTryJoiningGame(IPEndPoint ep, TryJoinningGameEvent e);

        protected abstract void OnPlayerJoinedGame(IPEndPoint ep, JoinGameEvent e);

        protected abstract void OnClientSendInputs(IPEndPoint ep, ClientInputsEvent e);

        protected abstract void OnServerDispatchedInputs(ServerDispathesInputsEvent e);

        protected abstract void OnServerReissuedInputs(ServerReissuesFramesEvent e);

        protected abstract void OnNetworkNotDisconnect(IPEndPoint ep);

        protected abstract void CheckDisconnection();

        /// <summary>
        /// 关闭通信
        /// </summary>
        public abstract void OnClose();
        #endregion
    }
}
